<?php

namespace App\Listeners;

use Firebase\JWT\JWT;
use Swof\Event\Listener;
use Swof\Server\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;

class HandshakeListener extends Listener
{
    protected $name = 'ws.hand';

    /**
     * Note:
     *
     * @param array $args
     * @return void|bool
     * @author: Zhao Zhiqiang
     * @since: 2021/6/6 11:29
     */
    public function handle($args = [])
    {
        $server = $args[0];
        $swoRequest = $args[1];
        $swoResponse = $args[2];

        // 这是接收websocket连接传递的参数
        $token = $swoRequest->header['sec-websocket-protocol'];

        if (! $token || ! $this->checkToken($server, $swoRequest, $token)) {
            $swoResponse->end();
            return false;
        }

        $this->handshake($swoRequest, $swoResponse);
    }

    protected function checkToken(Server $server, Request $swoRequest, string $token): bool
    {
        try {
            $fd = $swoRequest->fd;
            $jwtConfig = config('server.route.jwt');

            $jwt = JWT::decode($token, $jwtConfig['key'], $jwtConfig['algorithm']);

            $server->getRedis()->hSet($jwtConfig['key'], $jwt->data->uid, json_encode([
                'fd' => $fd,
                'name' => $jwt->data->name,
                'server_url' => $jwt->data->server_url
            ]));

            return true;
        } catch (\Exception $e) {
            dd($e->getFile(),$e->getLine(),$e->getMessage());
        }
    }

    /**
     * Note:
     *
     * @param Request $request
     * @param Response $response
     * @return false|void
     * @author: Zhao Zhiqiang
     * @since: 2021/6/6 11:29
     */
    protected function handshake(Request $request, Response $response)
    {
        $secWebSocketKey = $request->header['sec-websocket-key'];
        $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';
        if (0 === preg_match($patten, $secWebSocketKey)
            || 16 !== strlen(base64_decode($secWebSocketKey))) {
            $response->end();
            return false;
        }

        $key = base64_encode(
            sha1(
                $request->header['sec-websocket-key']
                . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
                true
            )
        );

        $headers = [
            'Upgrade' => 'websocket',
            'Connection' => 'Upgrade',
            'Sec-WebSocket-Accept' => $key,
            'Sec-WebSocket-Version' => '13',
        ];

        // WebSocket connection to 'ws://127.0.0.1:9502/'
        // failed: Error during WebSocket handshake:
        // Response must not include 'Sec-WebSocket-Protocol'
        // header if not present in request: websocket
        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }

        $response->status(101);
        $response->end();
    }
}